﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace myLittleC
{
    class Parser
    {
        protected ISyntaxObject obj = null;

        protected bool MayDeclaration {get; set;}

        protected Tokenizer mTokenizer;

        public Parser(String file)
        {
            MayDeclaration = false;
            mTokenizer = new Tokenizer(file);
        }

        public void parse()
        {
           
           //  Tokenizer.getNextToken(); //очень важная строчка для синтаксического анализатора
             if (mTokenizer.TokenType != TokenType.EOF)
                obj = parseStatement();
             if (mTokenizer.TokenType != TokenType.EOF)
                throw new ComExeption(ERRORS.BadExpression, mTokenizer.Position);
        }
       
        public  override String ToString() { return obj.ToString("", true); }
        
        protected virtual ExpType checkExpression(IExpression Expression)
        {
            return (Expression is UnaryExpression) ?
                typeUnaryExpression(Expression.Operation,
                    (Expression.Arguments[0] is IExpression) ?
                        checkExpression(Expression.Arguments[0] as UnaryExpression) : (Expression.Arguments[0] as IVarOrConst).ExType
                    )

                :
                 typeBinaryExpression(Expression.Operation, 
                    (Expression.Arguments[0] is IExpression) ? 
                        checkExpression(Expression.Arguments[0] as BinaryExpression) : (Expression.Arguments[0] as IVarOrConst).ExType,
                    (Expression.Arguments[0]is IExpression) ? 
                        checkExpression(Expression.Arguments[1] as BinaryExpression) : (Expression.Arguments[1] as IVarOrConst).ExType
                 );
        }

        protected virtual ExpType typeUnaryExpression(SubTokenType Operation, ExpType Operand) { return ExpType.None;}
        protected virtual ExpType typeBinaryExpression(SubTokenType Operation, ExpType OperandA, ExpType OperandB) { return ExpType.None; }       

        protected virtual bool isDeclareVareable(String Name) { return true; } 

        protected virtual ISyntaxObject parseStatement() { return parseExpr(); }
        
        private IPartOfExpession parseExpr() { return parseBinaryExpr(0); }

        private IPartOfExpession parseBinaryExpr(int prioritet)
        {
            IPartOfExpession left = (prioritet + 1 < 10) ? parseBinaryExpr(prioritet + 1) : ParseUnaryExpr();

            if (prioritet == 0 && ChekPrioritet(prioritet, mTokenizer.SubType) && !IsLValue(ref left))
                 throw new ComExeption(ERRORS.BadLValue, mTokenizer.Position);
            
            while (ChekPrioritet(prioritet, mTokenizer.SubType))
            {
                SubTokenType stt = mTokenizer.SubType;
                mTokenizer.getNextToken();
                IPartOfExpession right = parseBinaryExpr(prioritet + (prioritet == 0 ? 0 :1));
                
                if (right == null)
                    throw new ComExeption(((mTokenizer.SubType == SubTokenType.EOF) ? ERRORS.EOF : ERRORS.BadExpression), mTokenizer.Position);
                left = new BinaryExpression(stt, left, right);
            }
            return left;
        }

        IPartOfExpession ParseUnaryExpr()
        {
            IPartOfExpession parsedExpresion;

            if (mTokenizer.SubType ==  SubTokenType.SPTT_LPAR)
            {
                mTokenizer.getNextToken();
                parsedExpresion = parseExpr();

                if (mTokenizer.SubType != SubTokenType.SPTT_RPAR)
                    throw new ComExeption(ERRORS.UnExpRightParan, mTokenizer.Position);
               
                mTokenizer.getNextToken();
                return parsedExpresion;
            }

            else if (mTokenizer.TokenType == TokenType.Identeficator)
            {
               if(!isDeclareVareable(mTokenizer.ActiveToken.Value.ToString())) 
                    throw new ComExeption(ERRORS.DublDeclaration, mTokenizer.ActiveToken.Position);
            
                parsedExpresion = new SyntaxVar(mTokenizer.ActiveToken.Value as String);
                mTokenizer.getNextToken();

                if (mTokenizer.SubType == SubTokenType.Increment || mTokenizer.SubType == SubTokenType.Decrement)
                {
                    parsedExpresion = new UnaryExpression(mTokenizer.SubType, parsedExpresion, true);
                    mTokenizer.getNextToken();
                }
                else if (mTokenizer.TokenType == TokenType.Identeficator || mTokenizer.SubType == SubTokenType.SPTT_LPAR
                    || mTokenizer.TokenType == TokenType.ConstInt || mTokenizer.TokenType == TokenType.ConstFloat )
                    throw new ComExeption(ERRORS.BadExpression, mTokenizer.Position);
            }
                
            else if (mTokenizer.TokenType == TokenType.ConstInt || mTokenizer.TokenType == TokenType.ConstFloat)
            {
                parsedExpresion = new SyntaxConst(mTokenizer.ActiveToken);
                mTokenizer.getNextToken(); 
                
                if(mTokenizer.SubType == SubTokenType.Not)
                    throw new ComExeption(ERRORS.BadUnaryExp, mTokenizer.Position);
                else if (mTokenizer.TokenType == TokenType.Identeficator || mTokenizer.SubType == SubTokenType.SPTT_LPAR
                    || mTokenizer.TokenType == TokenType.ConstInt || mTokenizer.TokenType == TokenType.ConstFloat)
                    throw new ComExeption(ERRORS.BadExpression, mTokenizer.Position);
            }
            else if (mTokenizer.SubType == SubTokenType.Multiply || mTokenizer.SubType == SubTokenType.Add || mTokenizer.SubType == SubTokenType.Sub || ChekPrioritet(10, mTokenizer.SubType))
            {
                SubTokenType stt = mTokenizer.SubType;
                mTokenizer.getNextToken();
                parsedExpresion = ParseUnaryExpr();
                if (parsedExpresion == null)
                    throw new ComExeption((mTokenizer.TokenType == TokenType.EOF ?ERRORS.EOF : ERRORS.BadUnaryExp), mTokenizer.Position);

                parsedExpresion = new UnaryExpression(stt, parsedExpresion);
            }
            else throw new ComExeption(ERRORS.BadExpression, mTokenizer.Position);

            return parsedExpresion;
        }

        private bool ChekPrioritet(int Prioritet, SubTokenType SubType)
        {
            if (SubTokenType.Increment <= SubType && SubType <= SubTokenType.Not)
                return Prioritet == 10;
            else if (SubTokenType.Multiply <= SubType && SubType <= SubTokenType.Div)
                return Prioritet == 9;
            else if (SubTokenType.Add == SubType || SubType == SubTokenType.Sub)
                return Prioritet == 8;
            else if (SubTokenType.SHL == SubType || SubType == SubTokenType.SHR)
                return Prioritet == 7;
            else if (SubTokenType.Low <= SubType && SubType <= SubTokenType.HiOrEq)
                return Prioritet == 6;
            else if (SubTokenType.Equal == SubType || SubType == SubTokenType.NotEqual)
                return Prioritet == 5;
            else if (SubTokenType.And == SubType)
                return Prioritet == 4;
            else if (SubTokenType.Or == SubType)
                return Prioritet == 3;
            else if (SubTokenType.LogicalAnd == SubType)
                return Prioritet == 2;
            else if (SubTokenType.LogicalOr == SubType)
                return Prioritet == 1;
            else if (SubTokenType.Assignment <= SubType && SubType <= SubTokenType.DivAssigment)
                return Prioritet == 0;

            return false;
        }

        private bool IsLValue(ref IPartOfExpession left)
        {
            return (left is SyntaxVar || (left is UnaryExpression && (left as UnaryExpression).Operation == SubTokenType.Multiply));
        }
    }

    class ParseWithSimpleeDef : Parser
    {
        SimpleCharsTable table = new SimpleCharsTable();

        public ParseWithSimpleeDef(String file) :base(file) { MayDeclaration = true;}
        public new String ToString() { return table.ToString() + "\n" + ( obj == null ? " " : obj.ToString("", true) ); }

        protected bool isDeclaration { get { return true; } }

        protected override ISyntaxObject parseStatement()
        {
            while (MayDeclaration)            
            {
                String na = mTokenizer.ActiveToken.Name;
                mTokenizer.getNextToken();

                if (mTokenizer.ActiveToken.SubType != SubTokenType.Identeficator)
                    throw new ComExeption(ERRORS.BadDeclaration, mTokenizer.ActiveToken.Position);
                
                switch (na)
                {
                    case "var":
                        table.Add(mTokenizer.ActiveToken.Name, SimpleCharType.Variable);
                        break;
                    case "Struct": 
                        table.Add(mTokenizer.ActiveToken.Name, SimpleCharType.Struct);
                        break;
                    case "array": 
                        table.Add(mTokenizer.ActiveToken.Name, SimpleCharType.Array);
                        break;
                    case "procedure": 
                        table.Add(mTokenizer.ActiveToken.Name, SimpleCharType.Procedure);
                        break;
                    default:
                        throw new ComExeption(ERRORS.NotDefType, mTokenizer.Position);
                }
                mTokenizer.getNextToken();
                IsMayDeclaration();
            }
            return base.parseStatement();
        }

        private bool IsMayDeclaration()
        {
            String na = mTokenizer.ActiveToken.Name;
            MayDeclaration = (na == "var" || na == "Struct" || na == "array" || na == "procedure");
            return MayDeclaration;
        }

        protected override ExpType typeBinaryExpression(SubTokenType Operation, ExpType OperandA, ExpType OperandB)
        {

            return base.typeBinaryExpression(Operation, OperandA, OperandB);
        }

        protected override bool isDeclareVareable(String Name) { return table.find(Name) == -1 ? false : true; }
        
    }
}
